//    Copyright (C) Kherty.  All rights reserved.
using System.Diagnostics.Contracts;
using System.Text;
using OpenLS.Spreadsheet.Expressions;
using OpenLS.Spreadsheet.Parsing;

namespace OpenLS.Spreadsheet.UI.Internals
{
    internal sealed class FormulaCellsReference : FormulaReference
    {
        private CellReference _cellReference;
        private CellsRangeReference _rangeReference;

        internal FormulaCellsReference(Token token, CellsRangeReference rangeReference, Formula formula)
            : base(token, formula)
        {
            Contract.Requires(rangeReference != null);
            RangeReference = rangeReference;
        }

        internal FormulaCellsReference(Token token, CellReference cellReference, Formula formula) : base(token, formula)
        {
            Contract.Requires(cellReference != null);
            CellReference = cellReference;
        }

        public override Worksheet Worksheet
        {
            get
            {
                if (RangeReference != null)
                    return RangeReference.Worksheet;
                return CellReference.Worksheet;
            }
        }

        protected override Cell TopLeft
        {
            get
            {
                if (RangeReference != null) return RangeReference.Range.TopLeftCell;
                return CellReference.Cell;
            }
        }

        public override int RowSpan
        {
            get
            {
                if (RangeReference != null)
                    return RangeReference.Range.RowSpan;
                return CellReference.Cell.RowSpan;
            }
        }

        public override int ColumnSpan
        {
            get
            {
                if (RangeReference != null)
                    return RangeReference.Range.ColumnSpan;
                return CellReference.Cell.ColumnSpan;
            }
        }

        public override bool IsFullRow
        {
            get
            {
                if (RangeReference != null) return RangeReference.Range.IsFullRow;
                return false;
            }
        }

        public override bool IsFullColumn
        {
            get
            {
                if (RangeReference != null) return RangeReference.Range.IsFullColumn;
                return false;
            }
        }

        protected override string Label
        {
            get
            {
                if (RangeReference != null)
                    return RangeReference.Label;
                return CellReference.NameOrlabel;
            }
        }

        internal override string Text
        {
            get { return FullLabel; }
        }

        internal CellReference CellReference
        {
            get { return _cellReference; }
            set { _cellReference = value;
                Contract.Assert(RangeReference != null || value != null);
            }
        }

        private CellsRangeReference RangeReference
        {
            get { return _rangeReference; }
            set { _rangeReference = value;
                Contract.Assert(_rangeReference != null || CellReference != null);
            }
        }

        public override void ToggleLock()
        {
            if (_cellReference != null)
            {
                _cellReference = _cellReference.ToggleLock();
                NotifyChanged();
            }
        }

        public override string ToString()
        {
            var sb = new StringBuilder(base.ToString());
            if (CellReference != null)
                sb.Append((string) CellReference.Label);
            if (RangeReference != null)
                sb.Append(RangeReference.Label);
            return sb.ToString();
        }

        public override int GetHashCode()
        {
            if (RangeReference != null)
                return RangeReference.GetHashCode();
            return CellReference.GetHashCode();
        }

        internal override void Move(NavigationDirection dir, bool hold_)
        {
            try
            {
                if (hold_)
                {
                    if (RangeReference == null)
                    {
                        RangeReference = new SingleCellRangeReference(CellReference);
                    }

                    RangeReference = new MultiCellsRangeReference(RangeReference.TopLeft,
                                                                  RangeReference.BottomRight.Offset(GetOffset(dir)));
                    if (RangeReference.Range.Surface == 1)
                    {
                        var cell = RangeReference.Range.TopLeftCell;
                        CellReference = new DynamicCellReference(new AnchoredRowReference(cell.Row, false),
                                         new AnchoredColumnReference(cell.Column, false));

                        RangeReference = null;
                    }
                }
                else
                {
                    if (CellReference != null)
                    {
                        Cell cell = CellReference.Cell;
                        cell = cell.GetNeighbor(dir);
                        if (cell == null)
                            return;
                        Contract.Assert(cell != null);
                        CellReference = new DynamicCellReference(new AnchoredRowReference(cell.Row, false),
                                                                 new AnchoredColumnReference(cell.Column, false));
                        RangeReference = null;
                        return;
                    }
                }
            }
            finally
            {
                NotifyChanged();
            }
        }

        internal void SetCellReference(CellReference cr)
        {
            CellReference = cr;
            RangeReference = null;
            NotifyChanged();
        }

        internal override void Extend(CellAddress address, ResizerType resizerType)
        {
            try
            {
                if (CellReference != null)
                {
                    if (CellReference.Cell.Address.Equals(address))
                        return;
                    CellReference c2 = CellReference.Offset(address - CellReference.Address);
                    RangeReference = new MultiCellsRangeReference(CellReference, c2);
                    CellReference = null;
                    return;
                }
                CellsRangeReference cellsRangeReference = RangeReference; //\\
                CellReference cr1 = cellsRangeReference.TopLeft;
                CellReference cr2 = cr1.Offset(address - cr1.Cell.Address);
                if (cr1.Cell == cr2.Cell)
                {
                    CellReference = cr1;
                    RangeReference = null;
                    return;
                }

                RangeReference = new MultiCellsRangeReference(cr1, cr2);
                CellReference = null;
            }
            finally
            {
                NotifyChanged();
            }
        }

        internal override void Move(CellRelativeAddress address)
        {
            if (RangeReference != null)
                RangeReference = (CellsRangeReference) RangeReference.Offset(address);
            if (CellReference != null)
                CellReference = CellReference.Offset(address);
            NotifyChanged();
        }

        internal override RangeAddress AsRangeAddress()
        {
            if (RangeReference != null)
                return RangeReference.Range.Address;
            return new RangeAddress(CellReference.Cell.Address, CellReference.Cell.Address);
        }

        public override bool Equals(object obj)
        {
            if (obj == this)
                return true;
            var other = obj as FormulaCellsReference;
            if (other == null)
                return false;
            return Label == other.Label; //\\....
            //\\return object.Equals(cellReference, other.cellReference) && object.Equals(this.rangeReference, other.rangeReference);
        }
    }
}